﻿using System;
using System.Collections.Generic;
using Slc.Core.Enums;

namespace Slc.Core.Entity
{
    public class OQL1 : OQL4, IOQL1
    {
        private OQL CurrentOQL;


        private string GetWhereFields()
        {
            int count = CurrentOQL.fieldStack.Count;
            TableNameField[] tnfs = new TableNameField[count];
            for (int i = count - 1; i >= 0; i--)
                tnfs[i] = CurrentOQL.fieldStack.Pop();

            string[] fieldNames = new string[count];
            for (int i = 0; i < count; i++)
            {
                TableNameField tnf = tnfs[i];
                string sqlField = CurrentOQL.GetOqlFieldName(tnf);
                tnf.SqlFieldName = sqlField;
                string paraName = CurrentOQL.CreateParameter(tnf, tnf.Entity.PropertyList(tnf.Field));
                fieldNames[i] = string.Format("{0}={1}", sqlField, paraName);
            }
            return string.Join(" AND ", fieldNames);
        }

        public OQL1(OQL oql)
            : base(oql)
        {
            CurrentOQL = oql;
        }

        public OQL2 Where(OQLCondition condition)
        {
            CurrentOQL.sql_condition = condition.ConditionString;
            CurrentOQL.oqlString += "\r\n     WHERE " + CurrentOQL.sql_condition;
            return new OQL2(CurrentOQL);
        }

        /// <summary>
        /// 根据传入的查询参数数组，对字段名执行不区分大小写的比较，生成查询条件。
        /// 注意目前要求QueryParameter 用的是要查询的表的字段名称，而不是实体类的属性名称
        /// </summary>
        /// <param name="queryParas">查询参数数组</param>
        /// <returns>条件表达式</returns>
        public OQL2 Where(QueryParameter[] queryParas)
        {
            Dictionary<string, TableNameField> paras = CurrentOQL.Parameters;
            string[] fields = CurrentOQL.currEntity.PropertyNames;
            string str = "";
            int count = 0;
            foreach (QueryParameter para in queryParas)
            {
                foreach (string temp in fields)//比较字段是否在实体类中
                {
                    if (string.Compare(temp, para.FieldName, true) == 0)
                    {
                        string paraName = temp + (count++);
                        if (!paras.ContainsKey(paraName))
                        {
                            paras.Add(paraName, new TableNameField()
                            {
                                FieldValue = para.FieldValue,
                                Field = para.FieldName,
                                Entity = CurrentOQL.currEntity //必须指定
                            });
                            string cmpType = "";
                            switch (para.CompareType)
                            {
                                case enumCompare.Equal:
                                    cmpType = "=";
                                    break;
                                case enumCompare.Greater:
                                    cmpType = ">";
                                    break;
                                case enumCompare.Like:
                                    cmpType = " LIKE ";
                                    break;
                                case enumCompare.NoGreater:
                                    cmpType = "<=";
                                    break;
                                case enumCompare.NoSmaller:
                                    cmpType = ">=";
                                    break;
                                case enumCompare.NotEqual:
                                    cmpType = "<>";
                                    break;
                                case enumCompare.Smaller:
                                    cmpType = "<";
                                    break;
                                case enumCompare.IsNull:
                                    cmpType = " IS NULL ";
                                    break;
                                case enumCompare.IsNotNull:
                                    cmpType = " IS NOT NULL ";
                                    break;
                                default:
                                    cmpType = "=";
                                    break;
                            }
                            if (para.CompareType != enumCompare.IsNull && para.CompareType != enumCompare.IsNotNull)
                                str += " AND [" + temp + "]" + cmpType + "@" + paraName;
                            else
                                str += " AND [" + temp + "]" + cmpType;
                        }
                        break;
                    }
                }
            }

            if (str != "")
                str = str.Substring(" AND ".Length);

            CurrentOQL.sql_condition = str;
            CurrentOQL.oqlString += "\r\n     WHERE " + CurrentOQL.sql_condition;
            return new OQL2(CurrentOQL);
        }

        public OQL2 Where(OQLCompare cmpResult)
        {
            return GetOQL2ByOQLCompare(cmpResult);
        }

        public OQL2 Where(OQLCompareFunc cmpFun)
        {
            OQLCompare compare = new OQLCompare(this.CurrentOQL);
            OQLCompare cmpResult = cmpFun(compare);

            return GetOQL2ByOQLCompare(cmpResult);
        }

        public OQL2 Where<T>(OQLCompareFunc<T> cmpFun)
            where T : class
        {
            OQLCompare compare = new OQLCompare(this.CurrentOQL);
            T p1 = GetInstance<T>();

            OQLCompare cmpResult = cmpFun(compare, p1);
            return GetOQL2ByOQLCompare(cmpResult);
        }

        public OQL2 Where<T1, T2>(OQLCompareFunc<T1, T2> cmpFun)
            where T1 : EntityBase
            where T2 : EntityBase
        {
            OQLCompare compare = new OQLCompare(this.CurrentOQL);
            T1 p1 = GetInstance<T1>();
            T2 p2 = GetInstance<T2>();
            OQLCompare cmpResult = cmpFun(compare, p1, p2);
            return GetOQL2ByOQLCompare(cmpResult);
        }

        public OQL2 Where<T1, T2, T3>(OQLCompareFunc<T1, T2, T3> cmpFun)
            where T1 : EntityBase
            where T2 : EntityBase
            where T3 : EntityBase
        {
            OQLCompare compare = new OQLCompare(this.CurrentOQL);
            T1 p1 = GetInstance<T1>();
            T2 p2 = GetInstance<T2>();
            T3 p3 = GetInstance<T3>();
            OQLCompare cmpResult = cmpFun(compare, p1, p2, p3);
            return GetOQL2ByOQLCompare(cmpResult);
        }


        private OQL2 GetOQL2ByOQLCompare(OQLCompare cmpResult)
        {
            if (!object.Equals(cmpResult, null))
            {
                if (CurrentOQL != cmpResult.LinkedOQL)
                    throw new ArgumentException("OQLCompare 关联的OQL 对象不是当前OQL本身对象，请使用OQLCompareFunc或者它的泛型对象。");
                CurrentOQL.sql_condition = cmpResult.CompareString;
                CurrentOQL.oqlString += "\r\n     WHERE " + CurrentOQL.sql_condition;
            }
            return new OQL2(CurrentOQL);
        }
        private T GetInstance<T>() where T : class
        {
            T entity = this.CurrentOQL.GetUsedEntity<T>();
            if (entity == null)
                throw new ArgumentException(typeof(T).ToString() + " 类型的实例没有被OQL对象所使用");

            return entity;
        }

        #region 接口方法
        /// <summary>
        /// 使用实体类选定的属性作为查询条件和条件的值，必须有至少一个参数。该方法不可以多次调用。
        /// 如果想构造动态的查询条件，请使用OQLCompare 对象
        /// </summary>
        /// <param name="fields"></param>
        /// <returns></returns>
        public OQL2 Where(params object[] fields)
        {
            CurrentOQL.sql_condition = GetWhereFields();
            CurrentOQL.oqlString += "\r\n     WHERE " + CurrentOQL.sql_condition;
            return new OQL2(CurrentOQL);
        }
        public OQL3 GroupBy(object field)
        {
            string fieldName = CurrentOQL.TakeOneStackFields().SqlFieldName;
            CurrentOQL.GroupbyFieldNames.Add(fieldName.Trim());
            CurrentOQL.oqlString += "\r\n          GROUP BY " + fieldName;
            return new OQL3(CurrentOQL);
        }

        public OQL3 GroupBy(object field, params object[] others)
        {
            string strTemp = string.Empty;
            string fieldName = CurrentOQL.TakeOneStackFields().SqlFieldName;
            CurrentOQL.GroupbyFieldNames.Add(fieldName.Trim());

            for (int i = 0; i < others.Length; i++)
            {
                string fieldNameTemp = CurrentOQL.TakeOneStackFields().SqlFieldName;
                CurrentOQL.GroupbyFieldNames.Add(fieldNameTemp.Trim());
                strTemp += "," + fieldNameTemp;
            }

            CurrentOQL.oqlString += "\r\n          GROUP BY " + fieldName + strTemp;
            return new OQL3(CurrentOQL);
        }

        public OQL4 Having(object field, object Value, string sqlFunctionFormat)
        {
            OQL3 q3 = new OQL3(CurrentOQL);
            return q3.Having(field, Value, sqlFunctionFormat);
        }

        #endregion

        #region 聚合函数
        /// <summary>
        /// OQL1表达式之统计数量，请在结果实体类中使用PropertyList["字段别名"] 的方式获取查询值
        /// </summary>
        /// <param name="field">属性字段</param>
        /// <param name="asFieldName">别名，如果不指定，则使用字段名称</param>
        /// <returns>OQL1</returns>
        public OQL1 Count(object field, string asFieldName)
        {
            string currFieldName = CurrentOQL.TakeStackFields();
            if (string.IsNullOrEmpty(currFieldName))
                currFieldName = "*";
            return sqlFunction("COUNT", currFieldName, asFieldName);
        }

        /// <summary>
        /// OQL1表达式之求最大值，请在结果实体类中使用PropertyList["字段别名"] 的方式获取查询值
        /// </summary>
        /// <param name="field">属性字段</param>
        /// <param name="asFieldName">别名，如果不指定，则使用字段名称</param>
        /// <returns>OQL1</returns>
        public OQL1 Max(object field, string asFieldName)
        {
            string currFieldName = CurrentOQL.TakeStackFields();
            return sqlFunction("MAX", currFieldName, asFieldName);
        }

        /// <summary>
        /// OQL1表达式之求最小值，请在结果实体类中使用PropertyList["字段别名"] 的方式获取查询值
        /// </summary>
        /// <param name="field">属性字段</param>
        /// <param name="asFieldName">别名，如果不指定，则使用字段名称</param>
        /// <returns>OQL1</returns>
        public OQL1 Min(object field, string asFieldName)
        {
            string currFieldName = CurrentOQL.TakeStackFields();
            return sqlFunction("MIN", currFieldName, asFieldName);
        }

        /// <summary>
        /// OQL1表达式之求合计，请在结果实体类中使用PropertyList["字段别名"] 的方式获取查询值
        /// </summary>
        /// <param name="field">属性字段</param>
        /// <param name="asFieldName">别名，如果不指定，则使用字段名称</param>
        /// <returns>OQL1</returns>
        public OQL1 Sum(object field, string asFieldName)
        {
            string currFieldName = CurrentOQL.TakeStackFields();
            return sqlFunction("SUM", currFieldName, asFieldName);
        }

        /// <summary>
        /// OQL1表达式之求平均，请在结果实体类中使用PropertyList["字段别名"] 的方式获取查询值
        /// </summary>
        /// <param name="field">属性字段</param>
        /// <param name="asFieldName">字段别名，如果不指定，则使用字段名称</param>
        /// <returns>OQL1</returns>
        public OQL1 Avg(object field, string asFieldName)
        {
            string currFieldName = CurrentOQL.TakeStackFields();
            return sqlFunction("AVG", currFieldName, asFieldName);
        }

        private OQL1 sqlFunction(string sqlFunctionName, string fieldName, string asFieldName)
        {
            if (string.IsNullOrEmpty(asFieldName))
            {
                if (this.CurrentOQL.haveJoinOpt)
                    throw new Exception("有表连接查询的时候，" + sqlFunctionName + " 结果必须指定别名！");
                else
                    asFieldName = fieldName;

            }
            CurrentOQL.sqlFunctionString = sqlFunctionName + "(" + fieldName + ") AS " + asFieldName;

            this.CurrentOQL.currEntity.setProperty(asFieldName, 0);
            return this;
        }

        #endregion

    }
}
